/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2004
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
*
* Author's email : insane@dees.ufmg.br
* Author's Website : http://www.dees.ufmg.br/insane
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package br.ufmg.dees.insane.util.xfem;

import br.ufmg.dees.insane.util.IPoint3d;

/**
 * A class representing a Line in 2D. A line can
 * be represented by a point on it and a vector 
 * that have the same direction.
 * @author Kelson Wolff
 * @since December 2006
 */
public class Line {

	/**
	 * The origin of this Line
	 */
	protected IPoint3d origin; 
	
	/**
	 * The vector of orientation associated with this Line
	 */
	protected IPoint3d vector;
	
	/** 
	 * The constructor of this Line with parameters.
	 * @param origin The origin point. 
	 * @param vector The vector orientation. 
	 */
	public Line(IPoint3d origin, IPoint3d vector) {
		this.origin = origin;
		this.vector = vector;
	}
	
	/**
	 * The constructor of this Line without parameters. 
	 */
	public Line(){
		this.origin = new IPoint3d(0.0,0.0,0.0);
		this.vector = new IPoint3d(0.0,0.0,0.0);
	}

	/**
	 * @return Returns the origin.
	 */
	public IPoint3d getOrigin() {
		return this.origin;
	}

	/**
	 * @param origin The origin to set.
	 */
	public void setOrigin(IPoint3d origin) {
		this.origin = origin;
	}

	/**
	 * @return Returns the vector.
	 */
	public IPoint3d getVector() {
		return this.vector;
	}

	/**
	 * @param vector The vector to set.
	 */
	public void setVector(IPoint3d vector) {
		this.vector = vector;
	}
	
	/** Returns True if there is intersection between provided Line l and
	 * this Line or False if there isn't intersection.
	 * @param l The provided Line for test intersection with this Line.
	 * @return True if there is intersection between provided Line l and
	 * this Line or False if there isn't intersection.
	 */
	public boolean intersects(Line l){
		return false;
	}
	
	/** Returns True if there is intersection between provided Segment s and
	 * this Line or False if there isn't intersection.
	 * @param s The provided Segment for test intersection with this Line.
	 * @return True if there is intersection between provided Segment s and
	 * this Line or False if there isn't intersection.
	 */
	public boolean intersects(Segment s){
		boolean result = false;
		if((this.getVector().getX() * s.getVector().getY()) - (this.getVector().getY() * s.getVector().getX()) == 0){
			result = false;
		}	
		else{
			IPoint3d intersection = this.intersection(s);
			if(s.on(intersection))
				result = true;
		}
		return result;
	}
	
	/** Returns True if the provided point p is on this Line or False if
	 * it isn't.
	 * @param p The provided point for positioning test with this Line.
	 * @return True if the provided point p is on this Line or False if
	 * it isn't.
	 */
	public boolean on(IPoint3d p){
		return false;
	}
	
	/** Returns the intersection point between provided Line l and
	 * this Line if exists intersection between them.
	 * @param s The provided Line that intersects this Line.
	 * @return The intersection point between provided Line l and
	 * this Line if exists intersection between them.
	 */
	public IPoint3d intersection(Line l){
		return null;
	}
	
	/** Returns the intersection point between provided Segment s and
	 * this Line if exists intersection between them.
	 * @param s The provided Segment that intersects this Line.
	 * @return The intersection point between provided Segment s and
	 * this Line if exists intersection between them.
	 */
	public IPoint3d intersection(Segment s){
		double normS = Math.sqrt(Math.pow(s.getVector().getX(),2.0) + Math.pow(s.getVector().getY(),2.0)); 
		IPoint3d vectorU1 = new IPoint3d(s.getVector().getX()/normS , s.getVector().getY()/normS);
		
		double normL = Math.sqrt(Math.pow(this.getVector().getX(),2.0) + Math.pow(this.getVector().getY(),2.0));
		IPoint3d vectorU2 = new IPoint3d(this.getVector().getX()/normL , this.getVector().getY()/normL);
		
		IPoint3d vectorV = new IPoint3d(s.getFirstPoint().getX()-this.getOrigin().getX() , s.getFirstPoint().getY()-this.getOrigin().getY());
		IPoint3d vectorW = new IPoint3d(s.getSecondPoint().getX()-this.getOrigin().getX() , s.getSecondPoint().getY()-this.getOrigin().getY());
		
		double k = (vectorV.getX()*vectorU2.getX()) + (vectorV.getY()*vectorU2.getY());
		double xAux = vectorV.getX() - (k * vectorU2.getX());
		double yAux = vectorV.getY() - (k * vectorU2.getY());
		IPoint3d vectorVaux = new IPoint3d(xAux,yAux);
		double normVaux = Math.sqrt(Math.pow(vectorVaux.getX(),2.0) + Math.pow(vectorVaux.getY(),2.0));
		double tolerance = 1e-6;
		
		IPoint3d vectorU3 = new IPoint3d(0.0,0.0);
		if(Math.abs(normVaux) > tolerance)
			vectorU3 = new IPoint3d(vectorVaux.getX()/normVaux , vectorVaux.getY()/normVaux);
		
		double P = 0.0;
		double Q = 0.0;
		double normVectorU3 = Math.sqrt(Math.pow(vectorU3.getX(),2.0) + Math.pow(vectorU3.getY(),2.0));
		if(normVectorU3 == 0){
			if((vectorU2.getX()*vectorV.getY())-(vectorU2.getY()*vectorV.getX()) == 0){
				Q = Math.sqrt(Math.pow(vectorW.getX(),2.0) + Math.pow(vectorW.getY(),2.0) -
					Math.pow((vectorW.getX()*vectorU2.getX()+vectorW.getY()*vectorU2.getY()),2));
			}
			if((vectorU2.getX()*vectorW.getY())-(vectorU2.getY()*vectorW.getX()) == 0){
				P = Math.sqrt(Math.pow(vectorV.getX(),2.0) + Math.pow(vectorV.getY(),2.0) -
				Math.pow((vectorV.getX()*vectorU2.getX()+vectorV.getY()*vectorU2.getY()),2));
			}
		}
		else{
			P = (vectorV.getX()*vectorU3.getX()) + (vectorV.getY()*vectorU3.getY());
			Q = (vectorW.getX()*vectorU3.getX()) + (vectorW.getY()*vectorU3.getY());
		}
		
		double H = 0.0;
		double xI = 0.0;
		double yI = 0.0;
		
//		if((Q < -tolerance) || (Q > tolerance)){
//			if((P<0 && Q>0)||(P>0 && Q<0))
//				H = (Math.abs(P) * normS)/(Math.abs(Q) + Math.abs(P));
//			if((P<0 && Q<0)||(P>0 && Q>0))
//				H = -(Math.abs(P) * normS)/(Math.abs(Q) - Math.abs(P));
//			
//			xI = s.getFirstPoint().getX() + (H * vectorU1.getX());
//			yI = s.getFirstPoint().getY() + (H * vectorU1.getY());
//		}
//		else if(Math.abs(Q) <= tolerance){
//			xI = s.getSecondPoint().getX();
//			yI = s.getSecondPoint().getY();
//		}
		
		if((P<0 && Q>0)||(P>0 && Q<0))
			H = (Math.abs(P) * normS)/(Math.abs(Q) + Math.abs(P));
		if((P<0 && Q<0)||(P>0 && Q>0))
			H = -(Math.abs(P) * normS)/(Math.abs(Q) - Math.abs(P));
		
		xI = s.getFirstPoint().getX() + (H * vectorU1.getX());
		yI = s.getFirstPoint().getY() + (H * vectorU1.getY());
		
		IPoint3d intersection = new IPoint3d(xI,yI);
		
		return intersection;
	}

}
